#ifndef __SCENE_H__
#define __SCENE_H__

#include <iostream>
#include <list>
#include "observer.h"
#include "framework.h"
#include "controller/controller.h"
#include "controller/controller_2d.h"
#include "controller/controller3d.h"
#include "controller/top_controller.h"
#include "controller/height_controller.h"
#include "display_list_utils.h"
#include "model/height_model.h"
#include "model/top_model.h"
#include "model/perspective_model.h"
#include "view/top_view.h"
#include "view/height_view.h"
#include "view/perspective_view.h"



class Scene : public IFrameworkCallbacks {
	private:
		static const float light_color[4];
		static const float light_position[3];
		static const float light_ambient[4];

		typedef std::list<Controller*> ControllerList;
		ControllerList controllers;

	public:
		Scene(){}

		~Scene(){
			clean();
		}

		void clean(){
			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
				delete (*it);
		}

		void init(){
			DisplayListsUtils::instance().createDisplayLists();

			glClearColor (0.02, 0.02, 0.04, 0.0);
			glShadeModel (GL_SMOOTH);
			glEnable(GL_DEPTH_TEST);
			glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
			glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
			glLightfv(GL_LIGHT0, GL_POSITION, light_position);
			glEnable(GL_LIGHT0);
			glEnable(GL_LIGHTING);

			Framework& f = Framework::instance();

			// Vista Top
			Rectangle viewport(	f.getWindowWidth()*3/4,
								f.getWindowWidth(),f.getWindowHeight()/2,f.getWindowHeight());

			TopModel* m1 = new TopModel();
			TopView* v1 = new TopView(viewport,DisplayListsUtils::instance().getAxis2DTopHandle(),*m1);

			Controller2D* c1 = new TopController(v1,m1);
			controllers.push_back(c1);

			// Vista Altura
			Rectangle viewport3(	f.getWindowWidth()*3/4,
									f.getWindowWidth(),0,f.getWindowHeight()/2);

			HeightModel* m3 = new HeightModel();

			HeightView* v3 = new HeightView(viewport3,DisplayListsUtils::instance().getAxis2DHeightHandle(),*m3);

			Controller2D* c3 = new HeightController(v3,m3);
			controllers.push_back(c3);

			// Vista Perspectiva
			Rectangle viewport2(	0.0f,f.getWindowWidth()*3/4,
									0.0f,f.getWindowHeight());

			PerspectiveModel* pModel = new PerspectiveModel(*m3,*m1);

			PerspectiveView* v2 = new PerspectiveView(	viewport2,
														DisplayListsUtils::instance().getAxisHandle(),
														DisplayListsUtils::instance().getGridHandle(),*pModel);
			Controller3D* c2 = new Controller3D(v2,pModel);
			controllers.push_back(c2);
		}

		void display(){
			glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
				(*it)->display();
		}

		void keyboard(unsigned char key,int x ,int y){
			Framework& f = Framework::instance();
			processKey(key,x,y);

			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
				(*it)->keyboard(key,x,y);

			f.redisplay();
		}

		void processKey(unsigned char key,int x ,int y){
			switch(key){
				case 0x1b:
					clean();
					exit(0);
					break;
			}
		}

		void mousePressed(int key,int state,int x ,int y){
			Framework& f = Framework::instance();
			// Invierto y
			y =  (int)f.getWindowHeight()-y;

			Controller* controller = getViewportController(x,y);

			// El mouse dejo la ventana y no llego a llamarse
			// a entryFunc
			if(!controller)
				return;

			View& view = controller->getView();

			float newX = (float)(x-view.getViewport().x1)/view.getViewport().width();
			float newY = (float)(y-view.getViewport().y1)/view.getViewport().height();

			controller->mousePressed(key,state,newX,newY);

			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it){
				if (*it != controller)
					(*it)->notifyViewportLeave();
			}

			f.redisplay();
		}

		void mouseEntry(int state){
			// El mouse abandona todos los viewports, se les notifica
			Framework& f = Framework::instance();
			if(state==GLUT_LEFT){
				for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
					(*it)->notifyViewportLeave();
			}
			f.redisplay();
		}

		void mouseMotion(int x ,int y){
			Framework& f = Framework::instance();
			// Invierto y
			y =  (int)f.getWindowHeight()-y;
			Controller* controller = getViewportController(x,y);

			// El mouse dejo la ventana y no llego a llamarse
			// a entryFunc
			if(!controller)
				return;

			View& view = controller->getView();

			float newX = ((float)x-view.getViewport().x1)/view.getViewport().width();
			float newY = ((float)y-view.getViewport().y1)/view.getViewport().height();

			controller->mouseMotion(newX,newY);

			f.redisplay();
		}

		void reshape(int oldW,int oldH,int w ,int h){
			float ex = (float)w/(float)oldW;
			float ey = (float)h/(float)oldH;

			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
				(*it)->reshape(ex,ey);
		}

		Controller* getViewportController(int x, int y){
			for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it){
				if((*it)->getView().getViewport().intersect(x,y))
					return (*it);
			}
			return NULL;
		}
};

const float Scene::light_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
const float Scene::light_position[3] = {10.0f, 10.0f, 8.0f};
const float Scene::light_ambient[4] = {0.05f, 0.05f, 0.05f, 1.0f};

#endif /* __SCENE_H__ */
